﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Reflection;
using System.Threading;
using PubSubLite.Messaging.Threading;
using PubSubLite.Configuration;
using PubSubLite.Utils;

namespace PubSubLite.Messaging
{
    internal class MessageDispatcher : IMessageDispatcher
    {


        public MessageDispatcher(IBusConfiguration configuration, IMessageChannel channel)
            : this(configuration, channel, SubscribersStorage.Default)
        {

        }

        public MessageDispatcher(IBusConfiguration configuration, IMessageChannel channel, ISubscribersStorage storage)
        {
            Require.Argument.IsNotNull(configuration, "configuration");
            Require.Argument.IsNotNull(channel, "channel");
            Require.Argument.IsNotNull(storage, "storage");
            _channel = channel;
            _subscribers = storage;
            _configuration = configuration;
        }

        private IMessageChannel _channel;
        private ISubscribersStorage _subscribers;
        private IBusConfiguration _configuration;


        void IMessageDispatcher.DispatchMessage(IMessageBase message, Type messageType)
        {
            // on the "other side" of the channel, we have to use
            // reflection to recreate the generic type arguments.
            var method = typeof(ISubscribersStorage).GetMethod("GetSubscribers");
            method = method.MakeGenericMethod(new Type[] { messageType });
            var retVal = method.Invoke(_subscribers, null) as IEnumerable;
            var subscriberExists = false;
            if (retVal != null)
            {
                foreach (var subscriber in retVal)
                {
                    subscriberExists = true;
                    var subscriberType = subscriber.GetType().GetInterface("ISubscription`1");
                    var notify = subscriberType.GetMethod("Notify");
                    //notify.Invoke(subscriber, new object[]{ message });
                    var notification = new InvokeArgs(subscriber, notify, new object[] { message });
                    var invoker = new Invoker(_configuration);
                    invoker.Invoke(notification);
                }
            }
            if (!subscriberExists)
            {
                _configuration.LogWarning(string.Format("No subscribers found for type: {0}", messageType.Name));
            }
        }

    }
}
